﻿using System;
using System.Collections.Generic;
using System.Windows;
using GameEngine.Controller;
using GameEngine.Model;
using GameEngine.Model.Data;
using GameEngine.Model.Data.implementation;
using GameEngine.View;
using GameEngine.View.Components;
using PureMVC.Patterns;

namespace GameEngine
{
    
    //<summary>
    //A base Singleton <c>IFacade</c> implementation
    //</summary>
    //<remarks>
    //    <para>In PureMVC, the <c>Facade</c> class assumes these responsibilities:</para>
    //    <list type="bullet">
    //        <item>Initializing the <c>Model</c>, <c>View</c> and <c>Controller</c> Singletons</item>
    //        <item>Providing all the methods defined by the <c>IModel, IView, &amp; IController</c> interfaces</item>
    //        <item>Providing the ability to override the specific <c>Model</c>, <c>View</c> and <c>Controller</c> Singletons created</item>
    //        <item>Providing a single point of contact to the application for registering <c>Commands</c> and notifying <c>Observers</c></item>
    //    </list>
    public abstract class ApplicationFacade : Facade
    {
        // Notification constants. The Facade is the ideal
        // location for these constants, since any part
        // of the application participating in PureMVC 
        // Observer Notification will know the Facade.

        #region Notifications
        public const string FPS_NOTIFICATION = "fps";
        public const string ADDTHINGTOWORLD_NOTIFICATION = "ADDTHINGTOWORLD_NOTIFICATION";
        public const string REMOVETHINGFROMWORLD_NOTIFICATION = "REMOVETHINGFROMWORLD_NOTIFICATION";
        public const string SELECTTHING_NOTIFICATION = "SELECTTHING_NOTIFICATION";
        public const string CONTROLTHINGPOSITION_NOTIFICATION = "CONTROLTHINGPOSITION_NOTIFICATION";
        #endregion

        #region Command names
        //public const string PAGE_GAME_IN_COMMAND = "PAGE_GAME_IN_COMMAND";
        //public const string PAGE_GAME_OUT_COMMAND = "PAGE_GAME_OUT_COMMAND";

        internal const string EXIT_COMMAND = "exit";
        public const string TICK_COMMAND = "tick";
        
        internal const string CREATEGAME_COMMAND = "createGame";
        internal const string CREATEVIEW_COMMAND = "CREATEVIEW_COMMAND";
        internal const string CREATETHINGTYPE_COMMAND = "CREATETHINGTYPE_COMMAND";
        internal const string CREATERESOURCE_COMMAND = "CREATERESOURCE_COMMAND";
        internal const string CREATELEVEL_COMMAND = "createLevel";
        internal const string CREATEWORLD_COMMAND = "createWorld";
        internal const string CREATETHING_COMMAND = "createThing";

        internal const string DESTROYGAME_COMMAND = "destroyGame";
        internal const string DESTROYTHINGTYPE_COMMAND = "DESTROYTHINGTYPE_COMMAND";
        internal const string DESTROYVIEW_COMMAND = "DESTROYVIEW_COMMAND";
        internal const string DESTROYRESOURCE_COMMAND = "DESTROYRESOURCE_COMMAND";
        internal const string DESTROYLEVEL_COMMAND = "destroyLevel";
        internal const string DESTROYWORLD_COMMAND = "destroyWorld";
        internal const string DESTROYTHING_COMMAND = "destroyThing";

        //internal const string DESTROYWORLD_COMMAND = "destroyWorld";
        //public const string WORLDADDED_NOTIFICATION = "worldAdded";
        //public const string WORLDREMOVED_NOTIFICATION = "worldRemoved";
        #endregion
        
        public const string xmlContainer = "saves";

        public event Action OnInitializeController;
        public event Action OnInitializeModel;
        public event Action OnInitializeView;

        protected ApplicationFacade()
        {
            m_instance = this;
            CreateModelFacade();
            CreateViewFacade();
        }

        // Override Singleton Factory method 
        public new static ApplicationFacade Instance
        {
            get
            {
                /*if (m_instance == null)
                {
                    lock (m_staticSyncRoot)
                    {
                        if (m_instance == null) { 
                            m_instance = new ; 
                        }
                    }
                }*/
                return m_instance as ApplicationFacade;
            }
        }

        // optional initialization hook for Facade
        protected override void InitializeFacade()
        {
            base.InitializeFacade();
            // do any special subclass initialization here
        }

        // optional initialization hook for Controller
        protected override void InitializeController()
        {
            // call base to use the PureMVC Controller Singleton. 
            base.InitializeController();
            // do any special subclass initialization here
            // such as registering Commands
            if (OnInitializeController != null)
                OnInitializeController();
        }

        // optional initialization hook for Model
        protected override void InitializeModel()
        {
            // call base to use the PureMVC Model Singleton. 
            base.InitializeModel();

            // do any special subclass initialization here
            // such as creating and registering Model proxys
            // that don't require a facade reference at
            // construction time, such as fixed type lists
            // that never need to send Notifications.
            //RegisterProxy( new USStateNamesProxy() );

            // CAREFUL: Can't reference Facade instance in constructor 
            // of new Proxys from here, since this step is part of
            // Facade construction!  Usually, Proxys needing to send 
            // notifications are registered elsewhere in the app 
            // for this reason.
            if (OnInitializeModel != null)
                OnInitializeModel();

        }

        // optional initialization hook for View
        protected override void InitializeView()
        {
            // call base to use the PureMVC View Singleton. 
            base.InitializeView();

            // do any special subclass initialization here
            // such as creating and registering Mediators
            // that do not need a Facade reference at construction
            // time.
            //RegisterMediator( new LoginMediator() ); 

            // CAREFUL: Can't reference Facade instance in constructor 
            // of new Mediators from here, since this is a step
            // in Facade construction! Usually, all Mediators need 
            // receive notifications, and are registered elsewhere in 
            // the app for this reason.
            if (OnInitializeView != null)
                OnInitializeView();
        }

        public virtual ITileVO CreateTileVO()
        {
            return new TileVO();
        }

        //Usado para não obrigar o ApplicationFacade a conhecer os abstractos destes
        //TODO maneira de obrigar a ser dum determinado tipo (interface?)
        protected abstract void CreateViewFacade();

        protected abstract void CreateModelFacade();
    }

    public class ApplicationFacadeHelper<MyIdType>
        where MyIdType : IComparable<MyIdType>
    {
        public static void StartLevel(ILevelVOId id) {
            if (ModelFacade<MyIdType>.Instance.Engine.Game.CurrentLevel != null) {
                ModelFacade<MyIdType>.Instance.Engine.Game.CurrentLevel.Paused = true;
                DestroyLevel(ModelFacade<MyIdType>.Instance.Engine.Game.CurrentLevel.VO);
            }
            ApplicationFacadeHelper<MyIdType>.CreateLevel(ModelFacade<MyIdType>.Instance.Engine.Game.Levels[id]);
            ModelFacade<MyIdType>.Instance.Engine.Game.CurrentLevel = ModelFacade<MyIdType>.Helper.GetLevelDataProxy(id);
            ModelFacade<MyIdType>.Instance.Engine.Game.CurrentLevel.Paused = false;
        }

        public static IGameDataProxy<MyIdType> CreateGame(IGameVO<MyIdType> game)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.CREATEGAME_COMMAND, game);
            return ModelFacade<MyIdType>.Helper.GetGameDataProxy(game);
        }
        
        public static void DestroyGame() {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.DESTROYGAME_COMMAND, ModelFacade<MyIdType>.Instance.Game.VO);
        }

        public static void CreateThingType(IThingTypeVO<MyIdType> vo)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.CREATETHINGTYPE_COMMAND, vo);
        }

        public static void DestroyThingType(IThingTypeVO<MyIdType> vo)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.DESTROYTHINGTYPE_COMMAND, vo);
        }

        public static void CreateView(IViewVO<MyIdType> vo)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.CREATEVIEW_COMMAND, vo);
        }

        public static void DestroyView(IViewVO<MyIdType> vo)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.DESTROYVIEW_COMMAND, vo);
        }

        public static void CreateResource(IResourceVO vo)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.CREATERESOURCE_COMMAND, vo);
        }

        public static void DestroyResource(IResourceVO vo)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.DESTROYRESOURCE_COMMAND, vo);
        }

        public static ILevelDataProxy<MyIdType> CreateLevel(ILevelVO<MyIdType> level)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.CREATELEVEL_COMMAND, level);
            return ModelFacade<MyIdType>.Helper.GetLevelDataProxy(level);
        }

        public static void DestroyLevel(ILevelVO<MyIdType> level)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.DESTROYLEVEL_COMMAND, level);
        }

        public static void CreateWorld(IWorldVO<MyIdType> world)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.CREATEWORLD_COMMAND, world);
        }

        public static void DestroyWorld(IWorldVO<MyIdType> vo)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.DESTROYWORLD_COMMAND, vo);
        }

        internal static IThingDataProxy<MyIdType> CreateThing(IThingVO<MyIdType> thing, IWorldVOId worldId)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.CREATETHING_COMMAND, thing, worldId.Value);
            IThingDataProxy<MyIdType> thingDataProxy = ModelFacade<MyIdType>.Helper.GetThingDataProxy(thing);
            return thingDataProxy;
        }

        internal static void DestroyThing(IThingVOId<MyIdType> thing)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.DESTROYTHING_COMMAND, thing);
        }

        public static void ControlThingPosition(IThingVOId<MyIdType> thing)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.CONTROLTHINGPOSITION_NOTIFICATION, thing);
        }

        public static void SelectThing(IThingVOId<MyIdType> thing,IWorldVOId world)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.SELECTTHING_NOTIFICATION, thing, world.Value);
        }

        public static void AddThingToWorld(IThingVO<MyIdType> thing, IWorldVOId world)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.ADDTHINGTOWORLD_NOTIFICATION, thing, world.Value);
        }

        public static void RemoveThingFromWorld(IThingVOId<MyIdType> thing, IWorldVOId world)
        {
            ApplicationFacade.Instance.SendNotification(ApplicationFacade.REMOVETHINGFROMWORLD_NOTIFICATION, thing, world.Value);
        }
    }

    abstract public class ViewFacade<MyIdType, MyBaseUIElementType>
        where MyIdType : IComparable<MyIdType>
    {
        static ViewFacade<MyIdType, MyBaseUIElementType> instance;
        public static ViewFacade<MyIdType, MyBaseUIElementType> Instance
        {
            get
            {
                return instance;
            }
        }

        protected ViewFacade()
        {
            instance = this;
            //ApplicationFacade.Instance.OnInitializeController += new Action(Instance_OnInitializeController);
            //ApplicationFacade.Instance.OnInitializeModel += new Action(Instance_OnInitializeModel);
            //ApplicationFacade.Instance.OnInitializeView += new Action(Instance_OnInitializeView);
            //ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.REMOVEWORLD_COMMAND, typeof(RemoveWorldCommand<ItemVOId>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.CREATEWORLD_COMMAND, typeof(CreateWorldCommand<MyIdType, MyBaseUIElementType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.CREATEGAME_COMMAND, typeof(CreateGameCommand<MyIdType, MyBaseUIElementType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.CREATELEVEL_COMMAND, typeof(CreateLevelCommand<MyIdType, MyBaseUIElementType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.CREATETHINGTYPE_COMMAND, typeof(CreateThingTypeCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.CREATEVIEW_COMMAND, typeof(CreateViewCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.CREATERESOURCE_COMMAND, typeof(CreateResourceCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.CREATETHING_COMMAND, typeof(CreateThingCommand<MyIdType, MyBaseUIElementType>));

            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.ADDTHINGTOWORLD_NOTIFICATION, typeof(AddThingToWorldCommand<MyIdType, MyBaseUIElementType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.REMOVETHINGFROMWORLD_NOTIFICATION, typeof(RemoveThingFromWorldCommand<MyIdType, MyBaseUIElementType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.SELECTTHING_NOTIFICATION, typeof(SelectThingCommand<MyIdType, MyBaseUIElementType>));
            ApplicationFacade.Instance.RegisterProxy(ModelFacade<MyIdType>.Instance.CreateEngineDataProxy());
            ModelFacade<MyIdType>.Instance.InitializeWorldActions();
            ModelFacade<MyIdType>.Instance.InitializeThingTypeTypes();
            RegisterShapeTypes();
        }

        protected virtual void RegisterShapeTypes() { }

        #region Mediator Creators

        virtual public GameMediator<MyIdType, MyBaseUIElementType> 
            CreateGameMediator(
            IGameDataProxy<MyIdType> dataProxy, 
            //LevelMediator<MyLevelView, MyWorldView, MyMapView, MyThingView, MyIdType, MyBaseUIElementType> levelMed, 
            IGameView<MyIdType, MyBaseUIElementType> view)
        {
            return new GameMediator<MyIdType, MyBaseUIElementType>(
                        dataProxy,
                        //levelMed,
                        view);
        }

        virtual public LevelMediator<MyIdType, MyBaseUIElementType> 
            CreateLevelMediator(
            ILevelDataProxy<MyIdType> dataProxy, IDictionary<IWorldVOId, WorldMediator<MyIdType, MyBaseUIElementType>> worldMeds, ILevelView<MyIdType, MyBaseUIElementType> view)
        {
            return new LevelMediator<MyIdType, MyBaseUIElementType>(
                        dataProxy,
                        worldMeds,
                        view);
        }

        virtual public WorldMediator<MyIdType, MyBaseUIElementType> 
            CreateWorldMediator(
            IWorldDataProxy<MyIdType> world, 
            MapMediator<MyIdType, MyBaseUIElementType> mapMed,
            IThingMediator<MyIdType, MyBaseUIElementType> itemMed, IWorldView<MyIdType, MyBaseUIElementType> worldView)
        {
            return new WorldMediator<MyIdType, MyBaseUIElementType>(
                        world,
                        mapMed,
                        itemMed,
                        worldView);
        }

        virtual public MapMediator<MyIdType, MyBaseUIElementType>
            CreateMapMediator(IWorldDataProxy<MyIdType> world, IMapView<MyIdType, MyBaseUIElementType> mapView)
        {
            return new MapMediator<MyIdType, MyBaseUIElementType>(
                                mapView,
                                world
                            );
        }

        virtual public IThingMediator<MyIdType, MyBaseUIElementType> 
            CreateItemMediator(IWorldDataProxy<MyIdType> world)
        {
            return new ThingMediator<MyIdType, MyBaseUIElementType>(world);
        }

        virtual public MenuMediator<MenuType, MenuView>
            CreateMenuMediator<MenuType, MenuView>(string name, MenuView view,string outCommand)
            where MenuView : class, IMenuView<MenuType>
        {
            return new MenuMediator<MenuType, MenuView>(name, view, outCommand);
        }

        #endregion

        #region View Creators

        abstract public IGameView<MyIdType, MyBaseUIElementType> CreateGameView();

        abstract public ILevelView<MyIdType, MyBaseUIElementType> CreateLevelView();

        abstract public IWorldView<MyIdType, MyBaseUIElementType> CreateWorldView();

        abstract public IMapView<MyIdType, MyBaseUIElementType> CreateMapView();

        abstract public IThingView<MyIdType, MyBaseUIElementType> CreateViewInstance();

        #endregion

        public class Helper
        {
            public static MenuMediator<MenuType, MenuView>
            CreateMenu<MenuType, MenuView>(string name, MenuView view, string outCommand)
            where MenuView : class, IMenuView<MenuType>
            {
                var med = ViewFacade<MyIdType, MyBaseUIElementType>.Instance.CreateMenuMediator<MenuType, MenuView>(name, view, outCommand);
                Facade.Instance.RegisterMediator(med);
                return med;
            }

            #region Get Mediators

            public static GameMediator<MyIdType, MyBaseUIElementType> GetGameMediator() {
                return ApplicationFacade.Instance.RetrieveMediator(GameMediatorHelper<MyIdType>.GetMediatorNameFromProxy(
                    ModelFacade<MyIdType>.Instance.Game
                    )) as GameMediator<MyIdType, MyBaseUIElementType>;
            }

            public static LevelMediator<MyIdType, MyBaseUIElementType> GetLevelMediator(int id)
            {
                return ApplicationFacade.Instance.RetrieveMediator(LevelMediatorConstants<MyIdType>.GetMediatorNameFromProxy(
                    ApplicationFacade.Instance.RetrieveProxy(ILevelDataProxyConstants<MyIdType>.CreateDataProxyName(ModelFacade<MyIdType>.Instance.Game.VO.LevelOrder[id])) as ILevelDataProxy<MyIdType>
                    )) as LevelMediator<MyIdType, MyBaseUIElementType>;
            }

            public static LevelMediator<MyIdType, MyBaseUIElementType> GetLevelMediator(ILevelVOId id)
            {
                return ApplicationFacade.Instance.RetrieveMediator(LevelMediatorConstants<MyIdType>.GetMediatorNameFromProxy(
                    ApplicationFacade.Instance.RetrieveProxy(ILevelDataProxyConstants<MyIdType>.CreateDataProxyName(id)) as ILevelDataProxy<MyIdType>
                    )) as LevelMediator<MyIdType, MyBaseUIElementType>;
            }

            public static WorldMediator<MyIdType, MyBaseUIElementType> GetWorldMediator(IWorldVOId id)
            {
                string proxyName = IWorldDataProxyConstants<MyIdType>.CreateDataProxyName(id);
                var proxy = ApplicationFacade.Instance.RetrieveProxy(proxyName) as IWorldDataProxy<MyIdType>;
                var med = WorldMediatorConstants<MyIdType>.GetMediatorNameFromProxy(proxy);
                return ApplicationFacade.Instance.RetrieveMediator(med) as WorldMediator<MyIdType, MyBaseUIElementType>;
            }

            public static MapMediator<MyIdType, MyBaseUIElementType> GetMapMediator(IMapDataProxy<MyIdType> proxy)
            {
                return ApplicationFacade.Instance.RetrieveMediator(
                    MapMediatorConstants<MyIdType>.GetMediatorNameFromProxy(
                        proxy
                    )
                    ) as MapMediator<MyIdType, MyBaseUIElementType>;
            }

            public static IThingMediator<MyIdType, MyBaseUIElementType> GetWorldThingMediator(IWorldVOId id)
            {
                return GetWorldMediator(id).itemMediator;
            }

            #endregion

            #region Get Views

            public static IGameView<MyIdType, MyBaseUIElementType> GetGameView()
            {
                return GetGameMediator().ViewComponent;
            }

            public static ILevelView<MyIdType, MyBaseUIElementType> GetWorldView(int id)
            {
                return GetLevelMediator(id).ViewComponent;
            }

            public static IWorldView<MyIdType, MyBaseUIElementType> GetWorldView(IWorldVOId id)
            {
                return GetWorldMediator(id).ViewComponent;
            }

            public static IThingView<MyIdType, MyBaseUIElementType> GetThingView(IThingVOId<MyIdType> thing)
            {
                return GetWorldThingMediator(ModelFacade<MyIdType>.Helper.GetThingDataProxy(thing).WorldId).GetThingDataProxyView(
                    ModelFacade<MyIdType>.Helper.GetThingDataProxy(thing)
                );
            }

            #endregion

            public static void AddThingToWorld(IThingVO<MyIdType> thing, IWorldVOId world)
            {
                Facade.Instance.SendNotification(ApplicationFacade.ADDTHINGTOWORLD_NOTIFICATION, thing, world.Value);
                //ApplicationFacadeHelper<MyIdType>.CreateThing(thing);
                //IThingDataProxy<MyIdType> thingDataProxy = ModelFacade<MyIdType>.Helper.GetThingDataProxy(thing);
                //MyThingView view = ViewFacade<MyGameView, MyLevelView, MyWorldView, MyMapView, MyThingView, MyIdType, MyBaseUIElementType>.Instance.CreateViewInstance();
                //GetThingMediator(world.Id).AddThing(thingDataProxy, view);
                //ModelFacade<MyIdType>.Helper.GetWorldDataProxy(world).AddItem(thingDataProxy);
            }

            public static void RemoveThingFromWorld(IThingVOId<MyIdType> thing, IWorldVOId world)
            {
                Facade.Instance.SendNotification(ApplicationFacade.REMOVETHINGFROMWORLD_NOTIFICATION, thing, world.Value);
                //ApplicationFacadeHelper<MyIdType>.DestroyThing(thing);
                //ModelFacade<MyIdType>.Helper.GetWorldDataProxy(world).RemoveItem(
                //    ModelFacade<MyIdType>.Helper.GetThingDataProxy(thing));
                //GetWorldThingMediator(world.Id).RemoveThing(thing.Id);
            }
        }
    }

    abstract public class ModelFacade<MyIdType>
        where MyIdType : IComparable<MyIdType>
    {
        static ModelFacade<MyIdType> instance;
        public static ModelFacade<MyIdType> Instance
        {
            get
            {
                return instance;
            }
        }
        protected ModelFacade()
        {
            instance = this;
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.CONTROLTHINGPOSITION_NOTIFICATION, typeof(ControlThingPositionCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.TICK_COMMAND, typeof(TickCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(IThingDataProxyConstants<MyIdType>.THINGCHANGED_NOTIFICATION, typeof(ThingChangedCommand<MyIdType>));

            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.DESTROYGAME_COMMAND, typeof(DestroyGameCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.DESTROYLEVEL_COMMAND, typeof(DestroyLevelCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.DESTROYTHINGTYPE_COMMAND, typeof(DestroyThingTypeCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.DESTROYVIEW_COMMAND, typeof(DestroyViewCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.DESTROYRESOURCE_COMMAND, typeof(DestroyResourceCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.DESTROYWORLD_COMMAND, typeof(DestroyWorldCommand<MyIdType>));
            ApplicationFacade.Instance.RegisterCommand(ApplicationFacade.DESTROYTHING_COMMAND, typeof(DestroyThingCommand<MyIdType>));
            //GameDataProxy = game;
        }

        internal EngineDataProxy<MyIdType> Engine
        {
            get
            {
                return EngineDataProxy<MyIdType>.Instance;
                //return RetrieveProxy(EngineDataProxy.NAME) as EngineDataProxy; 
            }
        }

        public IGameDataProxy<MyIdType> Game
        {
            get
            {
                return Engine.Game;
            }
        }

        public IDictionary<string, Type> ThingTypeTypes
        {
            get
            {
                return Engine.ThingTypeTypes;
            }
        }

        public IDictionary<IShapeTypeVOId, IShapeTypeVO> ShapeTypes
        {
            get
            {
                return Engine.ShapeTypes;
            }
        }

        public virtual void InitializeThingTypeTypes() {
            ThingTypeTypes.Add("Thing",typeof(IThingTypeVO<MyIdType>));
            ThingTypeTypes.Add("Sensing Thing",typeof(ISensingThingTypeVO<MyIdType>));
            ThingTypeTypes.Add("Moving Thing",typeof(IMovingThingTypeVO<MyIdType>));
            ThingTypeTypes.Add("Thinking Thing",typeof(IThinkingThingTypeVO<MyIdType>));
        }

        public virtual void InitializeWorldActions()
        {
            EngineDataProxy<MyIdType>.Instance.AddWorldAction(
                    new EngineDataProxy<MyIdType>.WorldAction(
                //typeof(ISensingThingDataProxy<IdType>).Name,
                        typeof(ISensingThingDataProxy<MyIdType>),
                        SensingThingDataProxyActions<MyIdType>.Action,
                        1000
                        ));
            EngineDataProxy<MyIdType>.Instance.AddWorldAction(
                new EngineDataProxy<MyIdType>.WorldAction(
                //typeof(IMovingThingDataProxy<IdType>).Name,
                    typeof(IMovingThingDataProxy<MyIdType>),
                    MovingThingDataProxyActions<MyIdType>.Action,
                    100
                    ));
            EngineDataProxy<MyIdType>.Instance.AddWorldAction(
                new EngineDataProxy<MyIdType>.WorldAction(
                //typeof(IThinkingThingDataProxy<IdType>).Name,
                    typeof(IThinkingThingDataProxy<MyIdType>),
                    ThinkingThingDataProxyActions<MyIdType>.Action,
                    1000
                    ));
        }

        #region Proxy Creators

        virtual public IGameDataProxy<MyIdType> CreateGameDataProxy(IGameVO<MyIdType> vo)
        {
            var game = new GameDataProxy<MyIdType>(vo);
            return game;
        }

        virtual public IResourceDataProxy CreateResourceDataProxy(IResourceVO vo)
        {
            return new ResourceDataProxy(vo);
        }

        virtual public IWorldDataProxy<MyIdType> CreateWorldDataProxy(IWorldVO<MyIdType> vo)
        {
            return new WorldDataProxy<MyIdType>(vo);
        }

        virtual public IMapDataProxy<MyIdType> CreateMapDataProxy(IMapVO<MyIdType> vo, Point tileSize, float maxHeight)
        {
            return new MapDataProxy<MyIdType>(vo, tileSize, maxHeight);
        }

        /// <summary>
        /// Cria-se um para cada thing
        /// </summary>
        /// <param name="vo"></param>
        /// <returns></returns>
        virtual public IThingDataProxy<MyIdType> CreateThingDataProxy(IThingVO<MyIdType> vo)
        {
            if (vo.GetType().GUID == typeof(ThingVO<MyIdType>).GUID)
            {
                return new ThingDataProxy<MyIdType>(vo);
            }
            else if (vo.GetType().GUID == typeof(SensingThingVO<MyIdType>).GUID)
            {
                return new SensingThingDataProxy<MyIdType>(vo as ISensingThingVO<MyIdType>);
            }
            else if (vo.GetType().GUID == typeof(MovingThingVO<MyIdType>).GUID)
            {
                return new MovingThingDataProxy<MyIdType>(vo as IMovingThingVO<MyIdType>);
            }
            else if (vo.GetType().GUID == typeof(ThinkingThingVO<MyIdType>).GUID)
            {
                return new ThinkingThingDataProxy<MyIdType>(vo as IThinkingThingVO<MyIdType>);
            }
            throw new NotImplementedException();
        }

        public IThingDataProxy<MyIdType> CreateThingDataProxy(IThingTypeDataProxy<MyIdType> type,string Name)
        {
            return CreateThingDataProxy(type,Name);
        }

        public IThingDataProxy<MyIdType> CreateThingDataProxy(IThingTypeVOId type,string Name)
        {
            return CreateThingDataProxy(
                CreateThingVO(
                    new ThingVOId<MyIdType>(CreateNewId()) { Name = Name},
                    type));
        }

        /// <summary>
        /// Só se cria um de cada e logo de início
        /// </summary>
        /// <param name="vo"></param>
        /// <returns></returns>
        virtual public IThingTypeDataProxy<MyIdType> CreateThingTypeDataProxy(IThingTypeVO<MyIdType> vo)
        {
            if (vo.GetType().GUID == typeof(ThingTypeVO<MyIdType>).GUID)
            {
                return new ThingTypeDataProxy<MyIdType>(
                        vo,
                        CreateViewDataProxy(Engine.Game.Views[vo.ViewId]));
            }
            else if (vo.GetType().GUID == typeof(SensingThingTypeVO<MyIdType>).GUID)
            {
                return new SensingThingTypeDataProxy<MyIdType>(
                        vo as ISensingThingTypeVO<MyIdType>,
                        CreateViewDataProxy(Engine.Game.Views[vo.ViewId]));
            }
            else if (vo.GetType().GUID == typeof(MovingThingTypeVO<MyIdType>).GUID)
            {
                return new MovingThingTypeDataProxy<MyIdType>(
                        vo as IMovingThingTypeVO<MyIdType>,
                        CreateViewDataProxy(Engine.Game.Views[vo.ViewId]));
            }
            else if (vo.GetType().GUID == typeof(ThinkingThingTypeVO<MyIdType>).GUID)
            {
                return new ThinkingThingTypeDataProxy<MyIdType>(
                        vo as IThinkingThingTypeVO<MyIdType>,
                        CreateViewDataProxy(Engine.Game.Views[vo.ViewId]));
            }
            throw new NotImplementedException();
        }

        virtual public IViewDataProxy<MyIdType> CreateViewDataProxy(IViewVO<MyIdType> vo)
        {
            return new ViewDataProxy<MyIdType>(vo);
        }

        virtual public ILevelDataProxy<MyIdType> CreateLevelDataProxy(ILevelVO<MyIdType> vo)
        {
            return new LevelDataProxy<MyIdType>(vo);
        }

        /// <summary>
        /// Use this if you want to extend the default EngineDataProxy
        /// </summary>
        /// <returns></returns>
        virtual public EngineDataProxy<MyIdType> CreateEngineDataProxy()
        {
            return EngineDataProxy<MyIdType>.Instance;
        }

        #endregion

        #region VO Creators

        virtual public IGameVO<MyIdType> CreateGameVO(IGameVOId id)
        {
            return new GameVO<MyIdType>(id);
        }

        virtual public IGameVO<MyIdType> CreateGameVO()
        {
            return new GameVO<MyIdType>();
        }

        virtual public ILevelVO<MyIdType> CreateLevelVO(ILevelVOId id)
        {
            return new LevelVO<MyIdType>(id);
        }

        virtual public IWorldVO<MyIdType> CreateWorldVO(IWorldVOId id)
        {
            return new WorldVO<MyIdType>(id);
        }

        virtual public IViewVO<MyIdType> CreateViewVO(IViewVOId<MyIdType> id)
        {
            return new ViewVO<MyIdType>(id);
        }

        virtual public IShapeVO CreateItemViewShapeVO(IShapeTypeVO shapeType)
        {
            return new ShapeVO(new ShapeVOId("autoshapeId" + CreateNewId()), shapeType.Id);
        }

        virtual public IThingVO<MyIdType> CreateThingVO(IThingVOId<MyIdType> id, IThingTypeVOId typeId)
        {
            if (Engine.Game.ThingTypes[typeId].GetType().GUID == typeof(ThingTypeVO<int>).GUID)
            {
                return new ThingVO<MyIdType>(id, typeId);
            }
            else if (Engine.Game.ThingTypes[typeId].GetType().GUID == typeof(SensingThingTypeVO<int>).GUID)
            {
                return new SensingThingVO<MyIdType>(id, typeId);
            }
            else if (Engine.Game.ThingTypes[typeId].GetType().GUID == typeof(MovingThingTypeVO<int>).GUID)
            {
                return new MovingThingVO<MyIdType>(id, typeId);
            }
            else if (Engine.Game.ThingTypes[typeId].GetType().GUID == typeof(ThinkingThingTypeVO<int>).GUID)
            {
                return new ThinkingThingVO<MyIdType>(id, typeId);
            }
            throw new NotImplementedException();
        }

        static int thingTypeId = 0;
        virtual public IThingTypeVO<MyIdType> CreateThingTypeVO(string name, Type type, IViewVOId<MyIdType> viewId)
        {
            if(type.Equals(typeof(IThingTypeVO<MyIdType>)))
                return new ThingTypeVO<MyIdType>(new ThingTypeVOId(thingTypeId++) { Name = name })
                { 
                    ViewId = viewId 
                };
            else if (type.Equals(typeof(ISensingThingTypeVO<MyIdType>)))
                return new SensingThingTypeVO<MyIdType>(new ThingTypeVOId(thingTypeId++) { Name = name })
                {
                    ViewId = viewId
                };
            else if (type.Equals(typeof(IMovingThingTypeVO<MyIdType>)))
                return new MovingThingTypeVO<MyIdType>(new ThingTypeVOId(thingTypeId++) { Name = name })
                {
                    ViewId = viewId
                };
            else if (type.Equals(typeof(ISensingThingTypeVO<MyIdType>)))
                return new SensingThingTypeVO<MyIdType>(new ThingTypeVOId(thingTypeId++) { Name = name })
                {
                    ViewId = viewId
                };

            throw new NotSupportedException();
        }
        #endregion

        abstract public MyIdType CreateNewId();

        abstract public MyIdType ParseId(string id);

        public class Helper
        {
            #region Get Dataproxys
            public static IGameDataProxy<MyIdType> GetGameDataProxy(IGameVO<MyIdType> vo)
            {
                return ApplicationFacade.Instance.RetrieveProxy(IGameDataProxyConstants<MyIdType>.CreateDataProxyName(vo)) as IGameDataProxy<MyIdType>;
            }

            public static ILevelDataProxy<MyIdType> GetLevelDataProxy(ILevelVO<MyIdType> level)
            {
                return ApplicationFacade.Instance.RetrieveProxy(ILevelDataProxyConstants<MyIdType>.CreateDataProxyName(level)) as ILevelDataProxy<MyIdType>;
            }

            public static ILevelDataProxy<MyIdType> GetLevelDataProxy(ILevelVOId level)
            {
                return ApplicationFacade.Instance.RetrieveProxy(ILevelDataProxyConstants<MyIdType>.CreateDataProxyName(level)) as ILevelDataProxy<MyIdType>;
            }

            public static IWorldDataProxy<MyIdType> GetWorldDataProxy(IWorldVO<MyIdType> world)
            {
                return ApplicationFacade.Instance.RetrieveProxy(
                            IWorldDataProxyConstants<MyIdType>.CreateDataProxyName(world)) as IWorldDataProxy<MyIdType>;
            }

            public static IWorldDataProxy<MyIdType> GetWorldDataProxy(IWorldVOId world)
            {
                return ApplicationFacade.Instance.RetrieveProxy(
                            IWorldDataProxyConstants<MyIdType>.CreateDataProxyName(world)) as IWorldDataProxy<MyIdType>;
            }

            public static IThingDataProxy<MyIdType> GetThingDataProxy(IThingVO<MyIdType> thing)
            {
                return GetThingDataProxy(thing.Id);
            }

            public static IThingDataProxy<MyIdType> GetThingDataProxy(IThingVOId<MyIdType> id)
            {
                return ApplicationFacade.Instance.RetrieveProxy(
                            IThingDataProxyConstants<MyIdType>.CreateName(id)) as IThingDataProxy<MyIdType>;
            }

            public static IThingTypeDataProxy<MyIdType> GetThingTypeDataProxy(IThingTypeVO<MyIdType> thing)
            {
                return ApplicationFacade.Instance.RetrieveProxy(
                            IThingTypeDataProxyConstants<MyIdType>.CreateDataProxyName(thing)) as IThingTypeDataProxy<MyIdType>;
            }

            public static IThingTypeDataProxy<MyIdType> GetThingTypeDataProxy(IThingTypeVOId thing)
            {
                return ApplicationFacade.Instance.RetrieveProxy(
                            IThingTypeDataProxyConstants<MyIdType>.CreateDataProxyName(thing)) as IThingTypeDataProxy<MyIdType>;
            }

            public static IResourceDataProxy GetResourceDataProxy(IResourceVO thing)
            {
                return ApplicationFacade.Instance.RetrieveProxy(
                            IResourceDataProxyConstants.CreateDataProxyName(thing)) as IResourceDataProxy;
            }

            public static IResourceDataProxy GetResourceDataProxy(IResourceVOId thing)
            {
                return ApplicationFacade.Instance.RetrieveProxy(
                            IResourceDataProxyConstants.CreateDataProxyName(thing)) as IResourceDataProxy;
            }

            public static IViewDataProxy<MyIdType> GetViewDataProxy(IViewVO<MyIdType> thing)
            {
                return ApplicationFacade.Instance.RetrieveProxy(
                            IViewDataProxyConstants<MyIdType>.CreateDataProxyName(thing)) as IViewDataProxy<MyIdType>;
            }

            public static IViewDataProxy<MyIdType> GetViewDataProxy(IViewVOId<MyIdType> thing)
            {
                return ApplicationFacade.Instance.RetrieveProxy(
                            IViewDataProxyConstants<MyIdType>.CreateDataProxyName(thing)) as IViewDataProxy<MyIdType>;
            }
            #endregion

            #region Get VOs
            public static IViewVO<MyIdType> RetrieveViewVO(IViewVOId<MyIdType> id)
            {
                return ModelFacade<MyIdType>.Instance.Game.Views[id];            
            }

            public static IViewVOId<MyIdType> RetrieveViewFromThingType(IThingTypeVOId id)
            {
                return ModelFacade<MyIdType>.Instance.Game.ThingTypes[id].ViewId;
            }
            #endregion
        }
    }
}